/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
    ArkFile,
    ts,
    AstTreeUtils,
    ArkClass,
    ArkMethod,
    Stmt,
    ArkAssignStmt,
    ClassType,
    AbstractInvokeExpr
} from "arkanalyzer";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { FileMatcher, MatcherCallback, MatcherTypes, StmtMatcher } from '../../Index';
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { ArkAwaitExpr } from 'arkanalyzer/lib/core/base/Expr';

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'AwaitThenableCheck');
const gMetaData: BaseMetaData = {
    severity: 2,
    ruleDocPath: "docs/await-thenable.md",
    description: 'Unexpected `await` of a non-Promise (non-"Thenable") value.',
};

const promise = ['Promise', 'PromiseConstructor'];

export class AwaitThenableCheck implements BaseChecker {
    readonly metaData: BaseMetaData = gMetaData;
    public rule: Rule;
    public defects: Defects[] = [];
    public issues: IssueReport[] = [];
    private stmtMatcher: StmtMatcher = {
        matcherType: MatcherTypes.STMT,
        match(stmt: Stmt): boolean {
            return stmt instanceof ArkAssignStmt;
        }
    };

    public registerMatchers(): MatcherCallback[] {
        const fileMatcherCb: MatcherCallback = {
            matcher: this.stmtMatcher,
            callback: this.check
        }
        return [fileMatcherCb];
    }

    public check = (stmt: Stmt): void => {
        for (const use of stmt.getUses()) {
            if (use instanceof ArkAwaitExpr && this.isNonPromise(use)) {
                const position = stmt.getOriginPositionInfo();
                const filePath = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile().getFilePath();
                this.addIssueReportNode(position.getLineNo(), position.getColNo(), filePath);
                return;
            }
        }
    }

    private isNonPromise(awaitExpr: ArkAwaitExpr): boolean {
        let type = awaitExpr.getPromise().getType();
        return !(type instanceof ClassType && promise.includes(type.getClassSignature().getClassName()));

    }


    private addIssueReportNode(line: number, startCol: number, filePath: string): void {
        const severity = this.rule.alert ?? this.metaData.severity;
        let defect = new Defects(line, startCol, startCol, this.metaData.description, severity,
            this.rule.ruleId, filePath, this.metaData.ruleDocPath, true, false, false);
        this.issues.push(new IssueReport(defect, undefined));
    }
}